#include "EpollAwait.h"
#include <boost/lockfree/queue.hpp>
#include <map>
#include <mutex>
nco::Epoll::Epoll(EpollEvent e, int fd, int64_t timeout)
    : m_eve(e),
      m_fd(fd),
      m_timeout(timeout)
{
}
bool nco::Epoll::await_ready() const
{
    return false;
}
void nco::Epoll::await_resume()
{
}
void nco::Epoll::await_suspend(std::coroutine_handle<> handle)
{
    m_handle = handle;
    printf("%p\n", handle.address());
    EpollScheduler::add_Co(this);
}

int64_t nco::Epoll::getTimeout()
{
    return m_timeout;
}

void nco::Epoll::setTimeout(int64_t t)
{
    m_timeout = t;
}

int nco::Epoll::getFd()
{
    return m_fd;
}

nco::EpollEvent nco::Epoll::getEnv()
{
    return m_eve;
}

bool nco::Epoll::isTimeout()
{
    return m_isTimeout;
}

void nco::Epoll::setIsTimeout(bool b)
{
    m_isTimeout = b;
}

struct EpollCtl
{

    int m_efd;
    boost::lockfree::queue<nco::Epoll *> m_active;
    std::map<int64_t, nco::Epoll *> m_AwaitList;
    std::mutex m_mapMt;
    EpollCtl()
        : m_active(1024)
    {
        m_efd = epoll_create(1);
    }
    void add_env(nco::Epoll *c)
    {

        epoll_event ee;
        ee.data.ptr = (void *)c;
        ee.events = c->getEnv() | EPOLLET;
        epoll_ctl(m_efd, EPOLL_CTL_ADD, c->getFd(), &ee);
        if (c->getTimeout() > 0)
        {
            auto cur = clock() / (CLOCKS_PER_SEC / 1000.f);
            auto await = c->getTimeout() + cur;
            {
                std::lock_guard l{m_mapMt};
                while (m_AwaitList.find(await) != m_AwaitList.end())
                {
                    await++;
                }
                c->setTimeout(await);
                m_AwaitList[await] = c;
            }
        }
    }
    void doEpoll()
    {
        epoll_event eves[255];
        int num = epoll_wait(m_efd, eves, 255, 0);
        for (int i = 0; i < num; i++)
        {
            printf("get %d\n", num);
            auto ptr = (nco::Epoll *)eves[i].data.ptr;
            m_active.push(ptr);
            epoll_ctl(m_efd, EPOLL_CTL_DEL, ptr->getFd(), nullptr);
            ptr->setIsTimeout(false);
            {
                std::lock_guard l{m_mapMt};
                m_AwaitList.erase(ptr->getTimeout());
            }
        }

        {
            int64_t cur = (double)clock() / (CLOCKS_PER_SEC / 1000.f);
            std::lock_guard l{m_mapMt};
            for (auto &&[k, v] : m_AwaitList)
            {
                if (k <= cur)
                {
                    v->setIsTimeout(true);
                    m_active.push(v);
                    m_AwaitList.erase(k);
                }
                else
                {
                    break;
                }
            }
        }
    }
    bool hasActive()
    {
        return !m_active.empty();
    }
    nco::Epoll *getActive()
    {
        nco::Epoll *ret;
        m_active.consume_one([&](nco::Epoll *r)
                             { ret = r; });
        return ret;
    }
};

EpollCtl gCtl;

void nco::EpollScheduler::operator()()
{
    while (gCtl.hasActive())
    {
        auto active = gCtl.getActive();
        printf("%p\n", active->m_handle.address());
        active->m_handle.resume();
    }
    gCtl.doEpoll();
}

void nco::EpollScheduler::add_Co(Epoll *e)
{
    gCtl.add_env(e);
}
